﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.ComponentModel;
using Common.BaseClasses;
using PostSharp.Aspects;
using PostSharp.Extensibility;
using PostSharp.Reflection;

namespace Common.Aspects
{
    /// <summary>
    /// Adds DataContractAttribute to class and DataMemberAttribute to all public properties
    /// </summary>
    [MulticastAttributeUsage(MulticastTargets.Class, Inheritance = MulticastInheritance.Strict)]
    public class DataMemberAspect : TypeLevelAspect, IAspectProvider
    {
        private readonly string xmlNamespace;

        public DataMemberAspect(string xmlNamespace)
        {
            this.xmlNamespace = xmlNamespace;
        }

        public DataMemberAspect()
            : this(string.Empty)
        {

        }

        // This method is called at build time and should just provide other aspects.
        public IEnumerable<AspectInstance> ProvideAspects(object targetElement)
        {
            var targetType = (Type)targetElement;
            var introduceDataContractAspect =
                new CustomAttributeIntroductionAspect(
                    new ObjectConstruction(typeof(DataContractAttribute).GetConstructor(Type.EmptyTypes)));

            if (xmlNamespace != string.Empty)
                introduceDataContractAspect.CustomAttribute.NamedArguments.Add("Namespace", xmlNamespace);

            var introduceDataMemberAspect =
                new CustomAttributeIntroductionAspect(
                    new ObjectConstruction(typeof(DataMemberAttribute).GetConstructor(Type.EmptyTypes)));

            // Add the DataContract attribute to the type.
            yield return new AspectInstance(targetType, introduceDataContractAspect);

            // Add a DataMember attribute to every relevant property.)))
            foreach (var property in
                targetType.GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance)
                    .Where(property =>
                           property.CanWrite &&
                           !property.IsDefined(typeof(IgnoreDataMemberAttribute), false)))
                yield return new AspectInstance(property, introduceDataMemberAspect);
        }

    }
}
